Post

Replies

Boosts

Views

Activity

Reply to What is going on with transformable
@Jerome_Donfack There are two main requirements to use transformer in SwiftData: The type returned by transformedValueClass needs to correspond to the type you declare in the model, so it needs to be changed to NSArray.self transformedValue can only be encoded as NSData, and in CoreData, we can encode into any supported type, such as NSNumber, NSString Override class func transformedValueClass() -> AnyClass { Return NSArray.self // change to NSArray } BTW: If it is not to be compatible with your other custom data, SwiftData will use the built-in transaformer to encode [String] without using transformable
Jul ’25
Reply to Lazy init of @State objects using new Observable protocol
try this: @MainActor @propertyWrapper public struct LazyState<T: Observable>: @preconcurrency DynamicProperty { @State private var holder: Holder public var wrappedValue: T { holder.wrappedValue } public var projectedValue: Binding<T> { return Binding(get: { wrappedValue }, set: { _ in }) } public func update() { guard !holder.onAppear else { return } holder.setup() } public init(wrappedValue thunk: @autoclosure @escaping () -> T) { _holder = State(wrappedValue: Holder(wrappedValue: thunk())) } } extension LazyState { final class Holder { private var object: T! private let thunk: () -> T var onAppear = false var wrappedValue: T { object } func setup() { object = thunk() onAppear = true } init(wrappedValue thunk: @autoclosure @escaping () -> T) { self.thunk = thunk } } } // Demo struct LinkViewUsingLazyState: View { @LazyState var object = TestObservationObject() var body: some View { Text("LazyState") Text(object.name) @Bindable var o = object TextField("hello", text: $o.name) Button("change") { object.name = "\(Int.random(in: 0 ... 1000))" } } } @Observable class TestObservationObject { init() { print("init") } var name = "abc" }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’25
Reply to Setting multiple alignment guides in SwiftUI behaves strangely
You change center before bottom .alignmentGuide(VerticalAlignment.center) { dim in dim[.top] // re-assign center to 0 } .alignmentGuide(VerticalAlignment.top) { dim in dim[.bottom] } .alignmentGuide(VerticalAlignment.bottom) { dim in dim[VerticalAlignment.center] // 0 } In order to avoid mutual dependence, center value can not be used when calculating bottom. .alignmentGuide(VerticalAlignment.bottom) { dim in dim.height / 2 }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Dec ’24
Reply to Why first View on my NavigationStack appears again when I switch branch?
The problem is similar to the one described at https://forums.developer.apple.com/forums/thread/719521, where onAppear is triggered unexpectedly. The following patterns can currently be summarized: The navigation container must be within a conditional branch. The navigation container needs to perform certain operations (such as entering a new navigation page or creating multiple navigation container instances). Unexpected calls occur when switching to a branch that does not include the navigation container. This issue occurs only in iOS. I have submitted feedback as well. Details can be found here: https://fatbobman.com/en/posts/traps-and-countermeasures-for-abnormal-onappear-calls-in-swiftui/
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Dec ’24
Reply to onChange stops working after triggering navigationDestination(isPresented:destination:) within NavigationLink
I adjusted the code to the following form to better reflect the problem of onChange in a multi-layer nested navigation hierarchy. class ViewModel: ObservableObject { @Published var count = 0 } struct Root: View { @StateObject private var viewModel = ViewModel() var body: some View { NavigationStack { SubView(level: 1) .navigationDestination(for: Int.self) { value in SubView(level: value) } } .environmentObject(viewModel) } } struct SubView: View { let level: Int @EnvironmentObject var viewModel: ViewModel var body: some View { VStack { let _ = print("level \(level) Update: \(viewModel.count)") Text("Level: \(level)") Text("Count: \(viewModel.count)") Button("Count++") { viewModel.count += 1 } NavigationLink(value: level + 1) { Text("Goto New Level") } } .task(id: viewModel.count) { print("Level \(level) onTaskID: \(viewModel.count)") } .onChange(of: viewModel.count){ _ in print("Level \(level) onChange: \(viewModel.count)") } .onReceive(viewModel.$count){ _ in print("Level \(level) onReceive: \(viewModel.count)") } } } Clicking "Goto New Level" will add a level, and clicking "Count++" will increase the count value by one When the navigation exceeds 2 levels, only the bottom and top onChange closures will respond No matter how many levels of navigation there are, only the top task(id:) will respond No matter how many levels of navigation there are, onReceive at each level will respond Not sure if this is intentional or a bug, I will submit a feedback to Apple later
Topic: UI Frameworks SubTopic: SwiftUI
Nov ’24
Reply to Has anyone successfully used NSStagedMigrationManager?
I attempted to create a model by directly accessing the Managed Object Model (MOM), and the code ran successfully. I also completed the migration process. The implementation is somewhat obscure, and I believe Apple's main purpose in providing this migration method for Core Data is to offer a foundational implementation for phased migrations in SwiftData. guard let momdURL = Bundle.main.url(forResource: "Model", withExtension: "momd") else { fatalError() } let model1URL = momdURL.appending(component: "Model.mom") let model2URL = momdURL.appending(component: "Model 2.mom") guard let model1 = NSManagedObjectModel(contentsOf: model1URL) else { fatalError() } guard let model2 = NSManagedObjectModel(contentsOf: model2URL) else { fatalError() } let v1ModelChecksum = model1.versionChecksum let v1ModelReference = NSManagedObjectModelReference(model: model1, versionChecksum: v1ModelChecksum) let v2ModelChecksum = model2.versionChecksum let v2ModelReference = NSManagedObjectModelReference(model: model2, versionChecksum: v2ModelChecksum) let customStage = NSCustomMigrationStage( migratingFrom: v1ModelReference, to: v2ModelReference ) let migrationManager = NSStagedMigrationManager([customStage]) let description = container.persistentStoreDescriptions.first description?.setOption(migrationManager, forKey: NSPersistentStoreStagedMigrationManagerOptionKey) and I have update my topic about staged migration of Core Data. https://fatbobman.com/en/posts/what-s-new-in-core-data-in-wwdc23
Feb ’24
Reply to SwiftData with two Stores
This should be a bug. By using SwiftDataKit to inspect the information of NSPersistentStoreCoordinator, I found that only the store corresponding to the first configuration is loaded. // ModelContainer(for: Schema([Item.self,Tag.self]), configurations: [configuration2,configuration1]) let coordinator = context.coordinator print(coordinator?.persistentStores.count) // 1 Interestingly, SwiftData creates a separate sqlite file for each configuration. I suppose this has already been reported to Feedback, and now we can only wait for Apple to resolve this issue.
Dec ’23